package com.fyc.controller;

import com.fyc.dto.UserPointsReq;
import com.fyc.dto.UserRanking;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * projectName: com.fyc.controller
 *
 * @author: 顾司羽
 * time: 2025/2/14 15:27 周五
 * description:
 */
@RestController
public class UserRankingController {

    @Resource // 使用Resource注解注入StringRedisTemplate实例，用于操作Redis中字符串类型的数据
    private StringRedisTemplate stringRedisTemplate;


    /**
     * 处理添加用户积分的请求
     * 批量将用户积分数据写入到redis的zset有序集合中，以支持用户积分排行榜功能
     *
     * @param userPointsReqList 包含用户积分信息的列表，每个元素包含用户ID、积分分数和更新时间
     * @return 返回一个布尔值表示操作是否成功，此处固定返回true
     */
    @PostMapping("addUserPoint")
    public boolean addUserPoint(@RequestBody List<UserPointsReq> userPointsReqList) {
        // 定义Redis中存储用户积分排行榜的键
        String key = "user:ranking";
        // 遍历用户积分信息列表，为每个用户计算积分并添加到排行榜中
        for (UserPointsReq userPointsReq : userPointsReqList) {
            // 获取用户ID
            String userId = userPointsReq.getUserId();
            // 计算积分分数，分数为用户积分加上一个根据更新时间计算的衰减值，以1e13为基数进行衰减计算
            // 先按积分降序(从大到小)，积分相同时按照最后更新时间升序(从小到大)
            // 公式:score = 积分 + (1 - 时间戳/10的13次方)
            double score = userPointsReq.getPoints() + (1 - userPointsReq.getUpdateTime() / 1e13);
            // 将用户积分信息添加到Redis的有序集合中，自动根据分数排序
            this.stringRedisTemplate.opsForZSet().add(key, userId, score);
        }
        return true;
    }

    /**
     * 获取用户排名
     * 通过Redis的有序集合数据结构来获取用户排名前topN的数据
     *
     * @param topN 获取排名前topN的用户数量
     * @return 返回一个包含用户排名信息的列表
     */
    @GetMapping("userRankings")
    public List<UserRanking> userRankings(@RequestParam("topN") int topN) {
        // Redis中存储用户排名的键
        String key = "user:ranking";
        // 从Redis中获取排名前topN的用户数据，包括用户ID和分数
        Set<ZSetOperations.TypedTuple<String>> typedTuples = this.stringRedisTemplate.opsForZSet().reverseRangeWithScores(key, 0, topN - 1);
        // 创建一个列表来存储用户排名信息
        List<UserRanking> userRankingList = new ArrayList<>();
        // 遍历获取到的用户排名数据，转换为UserRanking对象并添加到列表中
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {

            UserRanking userRanking = new UserRanking();
            // 设置用户ID
            userRanking.setUserId(typedTuple.getValue());
            // 设置用户的分数，这个分数代表用户的排名依据
            userRanking.setRedisScore(typedTuple.getScore());
            // 将用户排名信息添加到列表中
            userRankingList.add(userRanking);
        }
        // 返回用户排名信息列表
        return userRankingList;
    }
}
